<script setup lang="ts">
import type {ClassType} from '@/typings/core';

import {computed, ref} from 'vue';

import {cn} from '@/utils/shared/utils';

import {ScrollArea, ScrollBar} from '../../ui';

interface Props {
  class?: ClassType;
  horizontal?: boolean;
  scrollBarClass?: ClassType;
  shadow?: boolean;
  shadowBorder?: boolean;
  shadowBottom?: boolean;
  shadowLeft?: boolean;
  shadowRight?: boolean;
  shadowTop?: boolean;
}

const props = withDefaults(defineProps<Props>(), {
  class: '',
  horizontal: false,
  shadow: false,
  shadowBorder: false,
  shadowBottom: true,
  shadowLeft: false,
  shadowRight: false,
  shadowTop: true,
});

const emit = defineEmits<{
  scrollAt: [{ bottom: boolean; left: boolean; right: boolean; top: boolean }];
}>();

const isAtTop = ref(true);
const isAtRight = ref(false);
const isAtBottom = ref(false);
const isAtLeft = ref(true);

const showShadowTop = computed(() => props.shadow && props.shadowTop);
const showShadowBottom = computed(() => props.shadow && props.shadowBottom);
const showShadowLeft = computed(() => props.shadow && props.shadowLeft);
const showShadowRight = computed(() => props.shadow && props.shadowRight);

const computedShadowClasses = computed(() => {
  return {
    'both-shadow':
        !isAtLeft.value &&
        !isAtRight.value &&
        showShadowLeft.value &&
        showShadowRight.value,
    'left-shadow': !isAtLeft.value && showShadowLeft.value,
    'right-shadow': !isAtRight.value && showShadowRight.value,
  };
});

function handleScroll(event: Event) {
  const target = event.target as HTMLElement;
  const scrollTop = target?.scrollTop ?? 0;
  const scrollLeft = target?.scrollLeft ?? 0;
  const offsetHeight = target?.offsetHeight ?? 0;
  const offsetWidth = target?.offsetWidth ?? 0;
  const scrollHeight = target?.scrollHeight ?? 0;
  const scrollWidth = target?.scrollWidth ?? 0;
  isAtTop.value = scrollTop <= 0;
  isAtLeft.value = scrollLeft <= 0;
  isAtBottom.value = scrollTop + offsetHeight >= scrollHeight;
  isAtRight.value = scrollLeft + offsetWidth >= scrollWidth;

  emit('scrollAt', {
    bottom: isAtBottom.value,
    left: isAtLeft.value,
    right: isAtRight.value,
    top: isAtTop.value,
  });
}
</script>

<template>
  <ScrollArea
      :class="[cn(props.class), computedShadowClasses]"
      :on-scroll="handleScroll"
      class="jcx-scrollbar relative"
  >
    <div
        v-if="showShadowTop"
        :class="{
        'opacity-100': !isAtTop,
        'border-border border-t': shadowBorder && !isAtTop,
      }"
        class="scrollbar-top-shadow pointer-events-none absolute top-0 z-10 h-12 w-full opacity-0 transition-opacity duration-300 ease-in-out will-change-[opacity]"
    ></div>
    <slot></slot>
    <div
        v-if="showShadowBottom"
        :class="{
        'opacity-100': !isAtTop && !isAtBottom,
        'border-border border-b': shadowBorder && !isAtTop && !isAtBottom,
      }"
        class="scrollbar-bottom-shadow pointer-events-none absolute bottom-0 z-10 h-12 w-full opacity-0 transition-opacity duration-300 ease-in-out will-change-[opacity]"
    ></div>
    <ScrollBar
        v-if="horizontal"
        :class="scrollBarClass"
        orientation="horizontal"
    />
  </ScrollArea>
</template>

<style scoped>
.jcx-scrollbar {
  &:not(.both-shadow).left-shadow {
    mask-image: linear-gradient(90deg, transparent, #000 16px);
  }

  &:not(.both-shadow).right-shadow {
    mask-image: linear-gradient(
        90deg,
        #000 0%,
        #000 calc(100% - 16px),
        transparent
    );
  }

  &.both-shadow {
    mask-image: linear-gradient(
        90deg,
        transparent,
        #000 16px,
        #000 calc(100% - 16px),
        transparent 100%
    );
  }
}

.scrollbar-top-shadow {
  background: linear-gradient(
      to bottom,
      hsl(var(--scroll-shadow, var(--background))),
      transparent
  );
}

.scrollbar-bottom-shadow {
  background: linear-gradient(
      to top,
      hsl(var(--scroll-shadow, var(--background))),
      transparent
  );
}
</style>
